package com.ycg.blog.utils;

import cn.hutool.core.lang.TypeReference;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;

import org.springframework.dao.DataAccessException;

import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author YCG
 * @date 2024-03-13 18:08
 * @description redis缓存封装
 */
@Component
public class CacheUtil {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 添加缓存并设置过期时间
     *
     * @param key
     * @param value
     * @param timeout
     * @param unit
     */
    public void setTTL(String key, Object value, long timeout, TimeUnit unit) {
        JSONConfig jsonConfig = new JSONConfig();
        jsonConfig.setDateFormat("yyyy-MM-DD");
        String jsonStr = JSONUtil.toJsonStr(value, jsonConfig);
        stringRedisTemplate.opsForValue().set(key, jsonStr, timeout, unit);
    }

    /**
     * 添加缓存
     *
     * @param key
     * @param value
     */
    public void set(String key, Object value) {
        JSONConfig jsonConfig = new JSONConfig();
        jsonConfig.setDateFormat("yyyy-MM-DD");
        String jsonStr = JSONUtil.toJsonStr(value, jsonConfig);
        stringRedisTemplate.opsForValue().set(key, jsonStr);
    }

    /**
     * 获取值
     *
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T get(String key, Class<T> clazz) {
        String json = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isBlank(json)) {
            return null;
        }
        return JSONUtil.toBean(json, clazz);
    }


    /**
     * 获取hash值
     * @param key
     * @param hashKey
     * @return hashKey对应的值
     */
    public String getHash(String key, String hashKey) {
        Object o = stringRedisTemplate.opsForHash().get(key, hashKey);
        if (o != null) {
            return o.toString();
        }
       return "";
    }
    /**
     * 获取hash值
     * @param key
     * @param clazz
     * @param ignores 忽略属性
     * @return hash对应的对象map
     */
    public <T> Map<String,Object> getHashMul(String key, Class<T> clazz, String... ignores) {
        List<String> fields = Arrays.stream(ClassUtil.getDeclaredFields(clazz))
                .filter(field -> {
                    if (ignores.length > 0) {
                        for (String ignore : ignores) {
                            return !field.getName().equals(ignore);
                        }
                    }
                    return true;
                })
                .map(Field::getName).toList();
        List<Object> objects = stringRedisTemplate.executePipelined(new SessionCallback<Object>() {
            @Override
            public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
                StringRedisTemplate template = (StringRedisTemplate) operations;
                for (String field : fields) {
                    template.opsForHash().get(key, field);
                }
                return null;
            }
        });
        HashMap<String, Object> map = new HashMap<>();
        for (int i = 0; i < fields.size(); i++) {
            map.put(fields.get(i), objects.get(i));
        }
        return map;
    }
    /**
     * 创建hash
     * @param key
     * @param hashKey
     * @param value
     */
    public void setHash(String key, String hashKey,String value) {
        stringRedisTemplate.opsForHash().put(key, hashKey, value);
    }

    /**
     * 批量插入hash
     * @param key
     * @param map
     */
    public void setHashMul(String key, Map<String,Object> map) {
        stringRedisTemplate.executePipelined(new SessionCallback<Object>() {
            @Override
            public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
                StringRedisTemplate template = (StringRedisTemplate) operations;
                map.forEach((k,v) -> {
                    template.opsForHash().put(key,k,String.valueOf(v));
                });
                return null;
            }
        });
    }

    /**
     * 如果没有key, 返回1 并且设置1秒过期时间, 有key则返回自增后的值
     * @param key
     * @return
     */
    public Long getAndIncrement(String key) {
        return (Long) stringRedisTemplate.executePipelined(new SessionCallback<>() {
            @Override
            public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
                StringRedisTemplate template = (StringRedisTemplate) operations;
                // 如果没有key, 调用getAndIncrement 并且设置过期时间
                template.opsForValue().increment(key);
                template.expire(key, 1, TimeUnit.SECONDS);
                return null;
            }
        }).get(0);
    }

    /**
     * 获取泛型
     *
     * @param key
     * @param type
     * @param <T>
     * @return
     */
    public <T> T get(String key, TypeReference<T> type) {
        String json = stringRedisTemplate.opsForValue().get(key);
        return JSONUtil.toBean(json, type, false);
    }


    /**
     * 删除一个
     *
     * @param key
     */
    public void removeOne(String key) {
        stringRedisTemplate.delete(key);
    }

    /**
     * 删除多个
     *
     * @param keys
     */
    public void removeBatch(Collection<String> keys) {
        stringRedisTemplate.delete(keys);
    }


    /**
     * 是否有key
     *
     * @param key
     * @return
     */
    public boolean hasKey(String key) {
        return Boolean.TRUE.equals(stringRedisTemplate.hasKey(key));
    }

    /**
     * 根据匹配模式删除缓存
     *
     * @param pattern
     */
    public void removeByPattern(String pattern) {
        Set<String> keys = stringRedisTemplate.keys(pattern);
        if (keys != null && keys.size() > 0) {
            stringRedisTemplate.delete(keys);
        }
    }
}

